using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Documents;
using System.Windows;
using System.Collections;
using System.Runtime.InteropServices;
using System.Windows.Forms.Integration;
using System.ComponentModel;
using Ricciolo.PaperBoy.UI.HTMLConverter;
using System.IO;
using System.Windows.Markup;
using System.Xml;
using System.Windows.Controls.Primitives;
using System.Windows.Controls;
using System.Diagnostics;
using System.Threading;
using System.Windows.Navigation;
//using Ricciolo.PaperBoy.UI.Interop;
using mshtml;
using System.Windows.Input;

namespace Ricciolo.PaperBoy.UI
{

    public class HtmlFlowDocumentReader : FlowDocumentReader // FlowDocumentScrollViewer
    {

        private System.Windows.Forms.WebBrowser browser;

        private ScrollViewer _viewer;
        private Decorator contentHost;
        private WebBrowserController controller;

        private MenuItem copyShortcutItem;
        private MenuItem openNewWindowItem;
        private Separator separatorItem;

        public static DependencyProperty HtmlProperty = DependencyProperty.Register("Html", typeof(string), typeof(HtmlFlowDocumentReader), new FrameworkPropertyMetadata(new PropertyChangedCallback(OnHtmlChanged)));
        public static DependencyProperty StatusTextProperty = FramePlus.StatusTextProperty.AddOwner(typeof(HtmlFlowDocumentReader));

        private static readonly RoutedUICommand CopyShortcutCommand = new RoutedUICommand("Copy shortcut", "CopyShortcut", typeof(HtmlFlowDocumentReader));
        private static readonly RoutedUICommand OpenNewWindowCommand = new RoutedUICommand("Open in new window", "OpenNewWindow", typeof(HtmlFlowDocumentReader));

        private static readonly RoutedEvent DocumentResetEvent = EventManager.RegisterRoutedEvent("DocumentReset", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(HtmlFlowDocumentReader));
        private static readonly RoutedEvent DocumentReadyEvent = EventManager.RegisterRoutedEvent("DocumentReady", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(HtmlFlowDocumentReader));

        public HtmlFlowDocumentReader()
        {
            this.CommandBindings.Add(new CommandBinding(CopyShortcutCommand, OnCopyShortcutCommandExecute));
            this.CommandBindings.Add(new CommandBinding(OpenNewWindowCommand, OnOpenNewWindowCommandExecute));

            browser = new System.Windows.Forms.WebBrowser();
            controller = new WebBrowserController(browser);
        }

        public override void BeginInit()
        {
            base.BeginInit();

            browser.DocumentText = String.Empty;
            browser.Navigated += new System.Windows.Forms.WebBrowserNavigatedEventHandler(browser_Navigated);
        }

        public override void EndInit()
        {
            base.EndInit(); 
        }

        public string Html
        {
            get { return (string)this.GetValue(HtmlProperty); }
            set { this.SetValue(HtmlProperty, value); }
        }

        public new Boolean CanGoToNextPage
        {
            get { return base.CanGoToNextPage || (_viewer != null && _viewer.VerticalOffset < _viewer.ScrollableHeight); }
        }

        public void PageDown()
        {
            NavigationCommands.NextPage.Execute(null, this);
        }

        public void ScrollToTop()
        {
            NavigationCommands.FirstPage.Execute(null, this);
        }

        public override void OnApplyTemplate()
        {
            contentHost = (Decorator)base.GetTemplateChild("PART_ContentHost");

            base.OnApplyTemplate();
        }

        private void PrepareView()
        {
            Control currentViewer = this.CurrentViewer;
            if (currentViewer == null) return;

            currentViewer.ApplyTemplate();
            // Find scroll viewer
            _viewer = currentViewer.Template.FindName("PART_ContentHost", currentViewer) as ScrollViewer;

            // Regist viewer for animation
            if (this.FindName("PART_viewer") != null)
                this.UnregisterName("PART_viewer");
            this.RegisterName("PART_viewer", currentViewer);

            separatorItem = new Separator();
            currentViewer.ContextMenu.Items.Insert(0, separatorItem);

            // Add commands
            copyShortcutItem = new MenuItem();
            copyShortcutItem.Header = "Copy shortcut";
            copyShortcutItem.Command = CopyShortcutCommand;
            currentViewer.ContextMenu.Items.Insert(0, copyShortcutItem);

            openNewWindowItem = new MenuItem();
            openNewWindowItem.Header = "Open in new window";
            openNewWindowItem.Command = OpenNewWindowCommand;
            currentViewer.ContextMenu.Items.Insert(0, openNewWindowItem);
        }

        private Control CurrentViewer
        {
            get { return (contentHost != null) ? (Control)this.contentHost.Child : null; }
        }

        protected override void SwitchViewingModeCore(FlowDocumentReaderViewingMode viewingMode)
        {
            base.SwitchViewingModeCore(viewingMode);

            PrepareView();
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);

            Inline line = e.Source as Inline;
            string link = String.Empty;

            // Find hyperlink
            while (line != null && (line = line.Parent as Inline) != null)
            {
                if (line is Hyperlink)
                {
                    link = ((Hyperlink) line).NavigateUri.ToString();
                    break;
                }
            }

            this.SetValue(FramePlus.StatusTextProperty, link);
        }

        protected override void OnContextMenuOpening(ContextMenuEventArgs e)
        {
            base.OnContextMenuOpening(e);

            Inline line = e.Source as Inline;
            bool found = false; ;

            // Find hyperlink
            while (line != null && (line = line.Parent as Inline) != null)
            {
                if (line is Hyperlink)
                {
                    found = true;
                    // Keep reference to hyperlink
                    copyShortcutItem.Tag = line;
                    break;
                }
            }

            // Hide or show item
            copyShortcutItem.Visibility = (found) ? System.Windows.Visibility.Visible : System.Windows.Visibility.Collapsed;
            separatorItem.Visibility = copyShortcutItem.Visibility;
            openNewWindowItem.Visibility = copyShortcutItem.Visibility;
        }

        private void OnCopyShortcutCommandExecute(object sender, ExecutedRoutedEventArgs e)
        {
            Hyperlink link = copyShortcutItem.Tag as Hyperlink;
            // Copy hyperlink to clipboard
            if (link != null)
                Clipboard.SetText(link.NavigateUri.ToString());
        }

        private void OnOpenNewWindowCommandExecute(object sender, ExecutedRoutedEventArgs e)
        {
            Hyperlink link = copyShortcutItem.Tag as Hyperlink;
            // Open link
            if (link != null)
                Process.Start(link.NavigateUri.ToString());
        }

        private static void OnHtmlChanged(DependencyObject element, DependencyPropertyChangedEventArgs e)
        {
            HtmlFlowDocumentReader doc = (HtmlFlowDocumentReader)element;

            doc.RaiseEvent(new RoutedEventArgs(DocumentResetEvent));
            doc.Document = null;

            if (e.NewValue != null)
            {
                // Parse html using IE
                doc.browser.DocumentText = String.Format("<html><body>{0}</body></html>", e.NewValue);
            }
        }

        private delegate void AddDocumentInvoker(XmlNode xamlNode);
        private Thread parserThread;

        void browser_Navigated(object sender, System.Windows.Forms.WebBrowserNavigatedEventArgs e)
        {
            // Skip initial document creation
            if (this.Document != null) return;

            IHTMLDOMNode node = (IHTMLDOMNode)browser.Document.Body.DomElement;
            if (!node.hasChildNodes())
            {
                // Add empty flowdocument
                this.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, new AddDocumentInvoker(this.AddDocument), null);
                return;
            }

            // Abort current parsing thread
            if (parserThread != null)
            {
                // Abort tread and wait
                Thread t = parserThread;
                parserThread.Abort();
                t.Join(100);
            }

            string xhtml;
             // Create memory string with xhtml
            using (StringWriter sw = new StringWriter())
            {
                // Create xml writer
                using (XmlTextWriter writer = new XmlTextWriter(sw))
                {
                    // Write annidate nodes
                    WriteNode(node, writer);
                    writer.Flush();
                }
                xhtml = sw.ToString();
            }

            // Create new thread
            parserThread = new Thread(ParseDom);
            parserThread.IsBackground = true;
            parserThread.SetApartmentState(ApartmentState.STA);
            parserThread.Name = "ParseDomThread";
            parserThread.Start(xhtml);
        }

        private void ParseDom(object obj)
        {
            try
            {
                try
                {
                    // Convert from xhtml to xaml
                    XmlNode xamlNode = HtmlToXamlConverter.ConvertHtmlToXamlElement(obj.ToString(), true);

                    // Add to flowdocument
                    this.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, new AddDocumentInvoker(this.AddDocument), xamlNode);
                }
                finally
                {
                    parserThread = null;
                }
            }
            catch (ThreadAbortException)
            { }
        }

        private void AddDocument(XmlNode xamlNode)
        {
            FlowDocument o;
            if (xamlNode == null)
                o = null;
            else
            {
                //Debug.WriteLine(xamlNode.OuterXml);
                using (XmlReader reader = new XmlNodeReader(xamlNode))
                {
                    // Parse xaml
                    try
                    {
                        o = XamlReader.Load(reader) as FlowDocument;
                    }
                    catch (XamlParseException xe)
                    {
                        o = new FlowDocument();
                        o.Blocks.Add(new Paragraph(new Run(xe.Message)));
                    }
                }
            }

            this.Document = o;
            this.RaiseEvent(new RoutedEventArgs(DocumentReadyEvent));
        }

        private static void WriteNode(IHTMLDOMNode node, XmlTextWriter writer)
        {
            // If it's a note text
            if (node.nodeType != 3)
            {
                writer.WriteStartElement(node.nodeName);
                // Write attributes
                IEnumerable attributes = (IEnumerable)node.attributes;
                foreach (IHTMLDOMAttribute attribute in attributes)
                {
                    if (attribute.specified && attribute.nodeValue != null)
                    {
                        string attr = attribute.nodeValue.ToString();
                        writer.WriteAttributeString(attribute.nodeName, attr);
                    }
                }
                // Write childnodes
                if (node.childNodes != null)
                {
                    foreach (IHTMLDOMNode childNode in (IEnumerable)node.childNodes)
                    {
                        if (childNode != null)
                            WriteNode(childNode, writer);
                    }
                }
                writer.WriteEndElement();
            }
            else if (node.nodeValue != null)
                writer.WriteString(node.nodeValue.ToString());
        }

    }

}
#if OLD
using System;
using System.Collections.Generic;
using System.Text;
using System.Windows.Documents;
using System.Windows;
using System.Collections;
using System.Runtime.InteropServices;
using System.Windows.Forms.Integration;
using System.ComponentModel;
using Ricciolo.PaperBoy.UI.HTMLConverter;
using System.IO;
using System.Windows.Markup;
using System.Xml;
using System.Windows.Controls.Primitives;
using System.Windows.Controls;
using System.Diagnostics;
using System.Threading;
using System.Windows.Navigation;
//using Ricciolo.PaperBoy.UI.Interop;
using mshtml;
using System.Windows.Input;

namespace Ricciolo.PaperBoy.UI
{

    public class HtmlFlowDocumentReader : FlowDocumentReader // FlowDocumentScrollViewer
    {

        private ScrollViewer _viewer;
        private Decorator contentHost;

        private MenuItem copyShortcutItem;
        private MenuItem openNewWindowItem;
        private Separator separatorItem;

        public static DependencyProperty HtmlProperty = DependencyProperty.Register("Html", typeof(string), typeof(HtmlFlowDocumentReader), new FrameworkPropertyMetadata(new PropertyChangedCallback(OnHtmlChanged)));
        public static DependencyProperty StatusTextProperty = FramePlus.StatusTextProperty.AddOwner(typeof(HtmlFlowDocumentReader));

        private static readonly RoutedUICommand CopyShortcutCommand = new RoutedUICommand("Copy shortcut", "CopyShortcut", typeof(HtmlFlowDocumentReader));
        private static readonly RoutedUICommand OpenNewWindowCommand = new RoutedUICommand("Open in new window", "OpenNewWindow", typeof(HtmlFlowDocumentReader));

        private static readonly RoutedEvent DocumentResetEvent = EventManager.RegisterRoutedEvent("DocumentReset", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(HtmlFlowDocumentReader));
        private static readonly RoutedEvent DocumentReadyEvent = EventManager.RegisterRoutedEvent("DocumentReady", RoutingStrategy.Bubble, typeof(RoutedEventHandler), typeof(HtmlFlowDocumentReader));

        public HtmlFlowDocumentReader()
        {
            this.CommandBindings.Add(new CommandBinding(CopyShortcutCommand, OnCopyShortcutCommandExecute));
            this.CommandBindings.Add(new CommandBinding(OpenNewWindowCommand, OnOpenNewWindowCommandExecute));
        }

        public string Html
        {
            get { return (string)this.GetValue(HtmlProperty); }
            set { this.SetValue(HtmlProperty, value); }
        }

        public new Boolean CanGoToNextPage
        {
            get { return base.CanGoToNextPage || (_viewer != null && _viewer.VerticalOffset < _viewer.ScrollableHeight); }
        }

        public void PageDown()
        {
            NavigationCommands.NextPage.Execute(null, this);
        }

        public void ScrollToTop()
        {
            NavigationCommands.FirstPage.Execute(null, this);
        }

        public override void OnApplyTemplate()
        {
            contentHost = (Decorator)base.GetTemplateChild("PART_ContentHost");

            base.OnApplyTemplate();
        }

        private void PrepareView()
        {
            Control currentViewer = this.CurrentViewer;
            if (currentViewer == null) return;

            currentViewer.ApplyTemplate();
            // Find scroll viewer
            _viewer = currentViewer.Template.FindName("PART_ContentHost", currentViewer) as ScrollViewer;

            // Regist viewer for animation
            if (this.FindName("PART_viewer") != null)
                this.UnregisterName("PART_viewer");
            this.RegisterName("PART_viewer", currentViewer);

            separatorItem = new Separator();
            currentViewer.ContextMenu.Items.Insert(0, separatorItem);

            // Add commands
            copyShortcutItem = new MenuItem();
            copyShortcutItem.Header = "Copy shortcut";
            copyShortcutItem.Command = CopyShortcutCommand;
            currentViewer.ContextMenu.Items.Insert(0, copyShortcutItem);

            openNewWindowItem = new MenuItem();
            openNewWindowItem.Header = "Open in new window";
            openNewWindowItem.Command = OpenNewWindowCommand;
            currentViewer.ContextMenu.Items.Insert(0, openNewWindowItem);
        }

        private Control CurrentViewer
        {
            get { return (contentHost != null) ? (Control)this.contentHost.Child : null; }
        }

        protected override void SwitchViewingModeCore(FlowDocumentReaderViewingMode viewingMode)
        {
            base.SwitchViewingModeCore(viewingMode);

            PrepareView();
        }

        protected override void OnMouseMove(MouseEventArgs e)
        {
            base.OnMouseMove(e);

            Inline line = e.Source as Inline;
            string link = String.Empty;

            // Find hyperlink
            while (line != null && (line = line.Parent as Inline) != null)
            {
                if (line is Hyperlink)
                {
                    link = ((Hyperlink) line).NavigateUri.ToString();
                    break;
                }
            }

            this.SetValue(FramePlus.StatusTextProperty, link);
        }

        protected override void OnContextMenuOpening(ContextMenuEventArgs e)
        {
            base.OnContextMenuOpening(e);

            Inline line = e.Source as Inline;
            bool found = false; ;

            // Find hyperlink
            while (line != null && (line = line.Parent as Inline) != null)
            {
                if (line is Hyperlink)
                {
                    found = true;
                    // Keep reference to hyperlink
                    copyShortcutItem.Tag = line;
                    break;
                }
            }

            // Hide or show item
            copyShortcutItem.Visibility = (found) ? System.Windows.Visibility.Visible : System.Windows.Visibility.Collapsed;
            separatorItem.Visibility = copyShortcutItem.Visibility;
            openNewWindowItem.Visibility = copyShortcutItem.Visibility;
        }

        private void OnCopyShortcutCommandExecute(object sender, ExecutedRoutedEventArgs e)
        {
            Hyperlink link = copyShortcutItem.Tag as Hyperlink;
            // Copy hyperlink to clipboard
            if (link != null)
                Clipboard.SetText(link.NavigateUri.ToString());
        }

        private void OnOpenNewWindowCommandExecute(object sender, ExecutedRoutedEventArgs e)
        {
            Hyperlink link = copyShortcutItem.Tag as Hyperlink;
            // Open link
            if (link != null)
                Process.Start(link.NavigateUri.ToString());
        }

        private static void OnHtmlChanged(DependencyObject element, DependencyPropertyChangedEventArgs e)
        {
            HtmlFlowDocumentReader doc = (HtmlFlowDocumentReader)element;

            doc.RaiseEvent(new RoutedEventArgs(DocumentResetEvent));
            doc.Document = null;

                        // Abort current parsing thread
            if (parserThread != null)
            {
                // Abort tread and wait
                Thread t = parserThread;
                parserThread.Abort();
                t.Join(100);
            }

            if (e.NewValue != null)
            {
                // Parse html using IE
                string htmlText = String.Format("<html xmlns=\"http://www.w3.org/1999/xhtml\"><body>{0}</body></html>", e.NewValue);

                // Create new thread
                parserThread = new Thread(doc.ParseDom);
                parserThread.IsBackground = true;
                parserThread.SetApartmentState(ApartmentState.STA);
                parserThread.Name = "ParseDomThread";
                parserThread.Start(htmlText);
            }
        }

        private delegate void AddDocumentInvoker(XmlNode xamlNode);
        private static Thread parserThread;

        private void ParseDom(object obj)
        {
            try
            {
                try
                {
                    using (StringReader reader = new StringReader(obj.ToString()))
                    {
                        using (Sgml.SgmlReader sgmlReader = new Sgml.SgmlReader())
                        {
                            sgmlReader.CaseFolding = Sgml.CaseFolding.ToLower;
                            sgmlReader.InputStream = reader;
                            sgmlReader.DocType = "html";
                            sgmlReader.WhitespaceHandling = WhitespaceHandling.Significant;

                            XmlDocument doc = new XmlDocument(sgmlReader.NameTable);
                            doc.Load(sgmlReader);

                            // Convert from xhtml to xaml
                            XmlNode xamlNode = HtmlToXamlConverter.ConvertHtmlToXamlElement(doc, true);

                            // Add to flowdocument
                            this.Dispatcher.Invoke(System.Windows.Threading.DispatcherPriority.Normal, new AddDocumentInvoker(this.AddDocument), xamlNode);
                        }
                    }
                }
                finally
                {
                    parserThread = null;
                }
            }
            catch (ThreadAbortException)
            { }
        }

        private void AddDocument(XmlNode xamlNode)
        {
            FlowDocument o;
            if (xamlNode == null)
                o = null;
            else
            {
                //Debug.WriteLine(xamlNode.OuterXml);
                using (XmlReader reader = new XmlNodeReader(xamlNode))
                {
                    // Parse xaml
                    try
                    {
                        o = XamlReader.Load(reader) as FlowDocument;
                    }
                    catch (XamlParseException xe)
                    {
                        o = new FlowDocument();
                        o.Blocks.Add(new Paragraph(new Run(xe.Message)));
                    }
                }
            }

            this.Document = o;
            this.RaiseEvent(new RoutedEventArgs(DocumentReadyEvent));
        }

    }

}
#endif